export namespace PokerConfig {

    export type EnumPoker =
        //红桃H  方片 F  梅花 M   黑桃 Black
        'H_A' | 'F_A' | 'M_A' | 'B_A' |
        'H_2' | 'F_2' | 'M_2' | 'B_2' |
        'H_3' | 'F_3' | 'M_3' | 'B_3' |
        'H_4' | 'F_4' | 'M_4' | 'B_4' |
        'H_5' | 'F_5' | 'M_5' | 'B_5' |
        'H_6' | 'F_6' | 'M_6' | 'B_6' |
        'H_7' | 'F_7' | 'M_7' | 'B_7' |
        'H_8' | 'F_8' | 'M_8' | 'B_8' |
        'H_9' | 'F_9' | 'M_9' | 'B_9' |
        'H10' | 'F10' | 'M10' | 'B10' |
        'H_J' | 'F_J' | 'M_J' | 'B_J' |
        'H_Q' | 'F_Q' | 'M_Q' | 'B_Q' |
        'H_K' | 'F_K' | 'M_K' | 'B_K' |
        // 大/小鬼
        'J_B' | 'J_S';


    export type EnumPokerNumber = 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 0;

    export const H_A: EnumPoker = 'H_A';
    export const H_2: EnumPoker = 'H_2';
    export const H_3: EnumPoker = 'H_3';
    export const H_4: EnumPoker = 'H_4';
    export const H_5: EnumPoker = 'H_5';
    export const H_6: EnumPoker = 'H_6';
    export const H_7: EnumPoker = 'H_7';
    export const H_8: EnumPoker = 'H_8';
    export const H_9: EnumPoker = 'H_9';
    export const H10: EnumPoker = 'H10';
    export const H_J: EnumPoker = 'H_J';
    export const H_Q: EnumPoker = 'H_Q';
    export const H_K: EnumPoker = 'H_K';

    export const F_A: EnumPoker = 'F_A';
    export const F_2: EnumPoker = 'F_2';
    export const F_3: EnumPoker = 'F_3';
    export const F_4: EnumPoker = 'F_4';
    export const F_5: EnumPoker = 'F_5';
    export const F_6: EnumPoker = 'F_6';
    export const F_7: EnumPoker = 'F_7';
    export const F_8: EnumPoker = 'F_8';
    export const F_9: EnumPoker = 'F_9';
    export const F10: EnumPoker = 'F10';
    export const F_J: EnumPoker = 'F_J';
    export const F_Q: EnumPoker = 'F_Q';
    export const F_K: EnumPoker = 'F_K';

    export const M_A: EnumPoker = 'M_A';
    export const M_2: EnumPoker = 'M_2';
    export const M_3: EnumPoker = 'M_3';
    export const M_4: EnumPoker = 'M_4';
    export const M_5: EnumPoker = 'M_5';
    export const M_6: EnumPoker = 'M_6';
    export const M_7: EnumPoker = 'M_7';
    export const M_8: EnumPoker = 'M_8';
    export const M_9: EnumPoker = 'M_9';
    export const M10: EnumPoker = 'M10';
    export const M_J: EnumPoker = 'M_J';
    export const M_Q: EnumPoker = 'M_Q';
    export const M_K: EnumPoker = 'M_K';

    export const B_A: EnumPoker = 'B_A';
    export const B_2: EnumPoker = 'B_2';
    export const B_3: EnumPoker = 'B_3';
    export const B_4: EnumPoker = 'B_4';
    export const B_5: EnumPoker = 'B_5';
    export const B_6: EnumPoker = 'B_6';
    export const B_7: EnumPoker = 'B_7';
    export const B_8: EnumPoker = 'B_8';
    export const B_9: EnumPoker = 'B_9';
    export const B10: EnumPoker = 'B10';
    export const B_J: EnumPoker = 'B_J';
    export const B_Q: EnumPoker = 'B_Q';
    export const B_K: EnumPoker = 'B_K';

    export const J_B: EnumPoker = 'J_B';
    export const J_S: EnumPoker = 'J_S';

    // 类型
    export type EnumPokerType = 0 | 100 | 200 | 300 | 400;

    export const Poker_Type_Base = 100;
    // 红桃 
    export const Poker_Type_H: EnumPokerType = 100;
    // 黑桃
    export const Poker_Type_B: EnumPokerType = 200;
    // 方片
    export const Poker_Type_F: EnumPokerType = 300;
    // 梅花
    export const Poker_Type_M: EnumPokerType = 400;

    // 数字
    export const Poker_Number_2: EnumPokerNumber = 2;
    export const Poker_Number_3: EnumPokerNumber = 3;
    export const Poker_Number_4: EnumPokerNumber = 4;
    export const Poker_Number_5: EnumPokerNumber = 5;
    export const Poker_Number_6: EnumPokerNumber = 6;
    export const Poker_Number_7: EnumPokerNumber = 7;
    export const Poker_Number_8: EnumPokerNumber = 8;
    export const Poker_Number_9: EnumPokerNumber = 9;
    export const Poker_Number10: EnumPokerNumber = 10;
    export const Poker_Number_J: EnumPokerNumber = 11;
    export const Poker_Number_Q: EnumPokerNumber = 12;
    export const Poker_Number_K: EnumPokerNumber = 13;
    export const Poker_Number_A: EnumPokerNumber = 14;

    export const Poker_Number_Array = [
        Poker_Number_2,
        Poker_Number_3,
        Poker_Number_4,
        Poker_Number_5,
        Poker_Number_6,
        Poker_Number_7,
        Poker_Number_8,
        Poker_Number_9,
        Poker_Number10,
        Poker_Number_J,
        Poker_Number_Q,
        Poker_Number_K,
        Poker_Number_A,
    ];


    export const pokerArray: EnumPoker[] = [
        // 红桃 H
        H_2,
        H_3,
        H_4,
        H_5,
        H_6,
        H_7,
        H_8,
        H_9,
        H10,
        H_J,
        H_Q,
        H_K,
        H_A,

        // 方片 F
        F_2,
        F_3,
        F_4,
        F_5,
        F_6,
        F_7,
        F_8,
        F_9,
        F10,
        F_J,
        F_Q,
        F_K,
        F_A,

        // 梅花 M
        M_2,
        M_3,
        M_4,
        M_5,
        M_6,
        M_7,
        M_8,
        M_9,
        M10,
        M_J,
        M_Q,
        M_K,
        M_A,

        // 黑桃 Black
        B_2,
        B_3,
        B_4,
        B_5,
        B_6,
        B_7,
        B_8,
        B_9,
        B10,
        B_J,
        B_Q,
        B_K,
        B_A,
        // 大/小鬼
        J_B,
        J_S,
    ] as const

    export const pokerValue = (type: EnumPoker) => {
        switch (type) {
            // 大/小鬼
            case J_B: return 0;
            case J_S: return 0;
            // 红桃 H
            case H_2: return Poker_Type_H + Poker_Number_2;
            case H_3: return Poker_Type_H + Poker_Number_3;
            case H_4: return Poker_Type_H + Poker_Number_4;
            case H_5: return Poker_Type_H + Poker_Number_5;
            case H_6: return Poker_Type_H + Poker_Number_6;
            case H_7: return Poker_Type_H + Poker_Number_7;
            case H_8: return Poker_Type_H + Poker_Number_8;
            case H_9: return Poker_Type_H + Poker_Number_9;
            case H10: return Poker_Type_H + Poker_Number10;
            case H_J: return Poker_Type_H + Poker_Number_J;
            case H_Q: return Poker_Type_H + Poker_Number_Q;
            case H_K: return Poker_Type_H + Poker_Number_K;
            case H_A: return Poker_Type_H + Poker_Number_A;

            // 黑桃 B
            case B_2: return Poker_Type_B + Poker_Number_2;
            case B_3: return Poker_Type_B + Poker_Number_3;
            case B_4: return Poker_Type_B + Poker_Number_4;
            case B_5: return Poker_Type_B + Poker_Number_5;
            case B_6: return Poker_Type_B + Poker_Number_6;
            case B_7: return Poker_Type_B + Poker_Number_7;
            case B_8: return Poker_Type_B + Poker_Number_8;
            case B_9: return Poker_Type_B + Poker_Number_9;
            case B10: return Poker_Type_B + Poker_Number10;
            case B_J: return Poker_Type_B + Poker_Number_J;
            case B_Q: return Poker_Type_B + Poker_Number_Q;
            case B_K: return Poker_Type_B + Poker_Number_K;
            case B_A: return Poker_Type_B + Poker_Number_A;

            // 方片 F
            case F_2: return Poker_Type_F + Poker_Number_2;
            case F_3: return Poker_Type_F + Poker_Number_3;
            case F_4: return Poker_Type_F + Poker_Number_4;
            case F_5: return Poker_Type_F + Poker_Number_5;
            case F_6: return Poker_Type_F + Poker_Number_6;
            case F_7: return Poker_Type_F + Poker_Number_7;
            case F_8: return Poker_Type_F + Poker_Number_8;
            case F_9: return Poker_Type_F + Poker_Number_9;
            case F10: return Poker_Type_F + Poker_Number10;
            case F_J: return Poker_Type_F + Poker_Number_J;
            case F_Q: return Poker_Type_F + Poker_Number_Q;
            case F_K: return Poker_Type_F + Poker_Number_K;
            case F_A: return Poker_Type_F + Poker_Number_A;

            // 梅花 M
            case M_2: return Poker_Type_M + Poker_Number_2;
            case M_3: return Poker_Type_M + Poker_Number_3;
            case M_4: return Poker_Type_M + Poker_Number_4;
            case M_5: return Poker_Type_M + Poker_Number_5;
            case M_6: return Poker_Type_M + Poker_Number_6;
            case M_7: return Poker_Type_M + Poker_Number_7;
            case M_8: return Poker_Type_M + Poker_Number_8;
            case M_9: return Poker_Type_M + Poker_Number_9;
            case M10: return Poker_Type_M + Poker_Number10;
            case M_J: return Poker_Type_M + Poker_Number_J;
            case M_Q: return Poker_Type_M + Poker_Number_Q;
            case M_K: return Poker_Type_M + Poker_Number_K;
            case M_A: return Poker_Type_M + Poker_Number_A;
        }
    }

    export const pokerNumberMin = () => {
        return Poker_Number_2;
    };

    export const pokerNumberMax = () => {
        return Poker_Number_A;
    };

    export const pokerNumber = (poker: EnumPoker) => {
        return pokerValue(poker) % Poker_Type_Base;
    };

    export const pokerEnumFromNumber = (value: number): EnumPoker[] => {
        value = value % Poker_Type_Base;
        if (value === 0) {
            return [PokerConfig.J_B, PokerConfig.J_S]
        }
        if (value >= pokerNumberMin() && value <= pokerNumberMax()) {
            // 2 3 --- 13 14
            return [pokerArray[value - 2 + (0 * 13)], pokerArray[value - 2 + (1 * 13)], pokerArray[value - 2 + (2 * 13)], pokerArray[value - 2 + (3 * 13)]];
        }
        throw `pokerFromNumber error value = ${value} `
    }

    export const pokerIsContain = (poker: EnumPoker, contain: EnumPoker[]) => {
        return contain.indexOf(poker) >= 0;
    }

    export const pokerFilter = (pokers: EnumPoker[], filter: EnumPoker[], includes: boolean = true) => {
        return pokers.filter((poker) => {
            return includes ? filter.indexOf(poker) >= 0 : !(filter.indexOf(poker) >= 0);
        });
    }

    // 检查是否是 大小鬼牌
    export const poker_joker_array: EnumPoker[] = [J_B, J_S];
    export const pokerIsJoker = (poker: EnumPoker) => {
        return pokerIsContain(poker, poker_joker_array);
    }

    export const pokerJokerFilter = (pokers: EnumPoker[], include: boolean = false) => {
        return pokerFilter(pokers, poker_joker_array, include)
    }

    export const pokerJokerCount = (pokers: EnumPoker[]) => {
        return pokers.length - pokerJokerFilter(pokers).length;
    }

    // A 的检查
    export const poker_ace_array: EnumPoker[] = [B_A, H_A, F_A, M_A];
    export const pokerIsAce = (poker: EnumPoker) => {
        return pokerIsContain(poker, poker_ace_array);
    }

    export const pokerAceFilter = (pokers: EnumPoker[], include: boolean = false) => {
        return pokerFilter(pokers, poker_ace_array, include)
    }

    export const pokerAceCount = (pokers: EnumPoker[]) => {
        return pokers.length - pokerAceFilter(pokers).length;
    }

    // 红桃 的检查
    export const pokerIsHPoker = (type: EnumPoker) => {
        return type.charAt(0) == H_A.charAt(0)
    }
    // 方片 的检查
    export const pokerIsFPoker = (type: EnumPoker) => {
        return type.charAt(0) == F_A.charAt(0)
    }
    // 梅花 的检查
    export const pokerIsMPoker = (type: EnumPoker) => {
        return type.charAt(0) == M_A.charAt(0)
    }
    // 黑桃 的检查
    export const pokerIsBPoker = (type: EnumPoker) => {
        return type.charAt(0) == B_A.charAt(0)
    }

    // Min的检查
    const poker_min: EnumPoker[] = [B_2, H_2, F_2, M_2];
    export const pokerIsMin = (poker: EnumPoker) => {
        return pokerIsContain(poker, poker_min);
    }

    export const pokerMinFilter = (pokers: EnumPoker[]) => {
        return pokerFilter(pokers, poker_min, false)
    }

    export const pokerMinCount = (pokers: EnumPoker[]) => {
        return pokers.length - pokerMinFilter(pokers).length;
    }

    export const pokerIsMinOrAce = (poker: EnumPoker) => {
        return pokerIsMin(poker) || pokerIsAce(poker);
    }

    /**
     * 推荐一个小于当前的 如果没有,则推荐自己
     */
    export const pokerPrev = (poker: EnumPoker): number => {
        const value = pokerNumber(poker);
        // 如果是鬼牌,则是自己
        if (value === 0) {
            return value;
        }

        if (value >= Poker_Number_2 && value <= Poker_Number_A) {
            // 2 >> 0 
            const indexPrev = value - Poker_Number_2 - 1;
            if (indexPrev < 0 || indexPrev >= Poker_Number_Array.length) {
                return value;
            }
            return Poker_Number_Array[indexPrev]
        }

        return value;
    }

    /**
    * 推荐一个大于当前的 如果没有,则推荐自己
    */
    export const pokerNext = (poker: EnumPoker): number => {
        const value = pokerNumber(poker);
        // 如果是鬼牌,则是自己
        if (value === 0) {
            return value;
        }

        if (value >= Poker_Number_2 && value <= Poker_Number_A) {
            // 2 >> 0 
            // 3 >> 1
            const indexPrev = value - 1;
            if (indexPrev < 0 || indexPrev >= Poker_Number_Array.length) {
                return value;
            }
            return Poker_Number_Array[indexPrev]
        }

        return value;
    }

    export const pokerIcon = (poker: EnumPoker) => {
        const value = pokerNumber(poker);
        if (value === 0) {
            return '🃏'
        }
        const type = pokerValue(poker) - value;
        switch (type) {
            case Poker_Type_H: return '♥️' + (value === 14 ? "A" : value);
            case Poker_Type_F: return '♦️' + (value === 14 ? "A" : value);
            case Poker_Type_M: return '♣️' + (value === 14 ? "A" : value);
            case Poker_Type_B: return '♠️' + (value === 14 ? "A" : value);
        }
    }

    /**
     * poker 出牌推荐
     * 根据已有的进行推荐 最多支持三张
     * @param poker 
     */
    export const pokerHint = (pokerArray: EnumPoker[]): EnumPoker[][] => {
        if (pokerArray.length === 1) {
            const poker = pokerArray[0];
            return [
                pokerEnumFromNumber(pokerPrev(poker)),
                pokerEnumFromNumber(pokerNumber(poker)),
                pokerEnumFromNumber(pokerNext(poker)),
            ]
        }

        if (pokerArray.length === 2) {
            const poker1Number = pokerNumber(pokerArray[0]);
            const poker2Number = pokerNumber(pokerArray[1]);
            const space = Math.abs(poker1Number - poker2Number);
            const pokerMin = Math.min(poker1Number, poker2Number);
            const pokerMax = Math.max(poker1Number, poker2Number);

            if (space === 0) {
                // 2 2 -> 2 = 2 2 2 
                return [pokerEnumFromNumber(poker1Number)]
            } else if (space === 1) {
                if (pokerMin === Poker_Number_2) {
                    // 2 3 -> 4 = 2,3,4
                    return [pokerEnumFromNumber(pokerMax + 1)]
                } else if (pokerMax === Poker_Number_A) {
                    // K A -> Q = Q,K,A
                    return [pokerEnumFromNumber(pokerMin - 1)]
                } else {
                    // 3 4 -> 2,5 = 2,3,4 | 3,4,5
                    return [pokerEnumFromNumber(pokerMin - 1), pokerEnumFromNumber(pokerMax + 1),]
                }
            } else if (space === 2) {
                // 2 4 -> 3
                return [pokerEnumFromNumber(Math.min(poker1Number, poker2Number) + 1)]
            } else if (space === 3) {
                // 2 5 -> 3 4
                return [
                    pokerEnumFromNumber(pokerMin + 1),
                    pokerEnumFromNumber(pokerMax - 1),
                ]
            }
        }

        if (pokerArray.length === 3) {
            const pokerNumerArray = pokerArray.map(v => pokerNumber(v)).sort();
            const pokerMin = pokerNumerArray[0];
            const pokerMax = pokerNumerArray[2];
            // 判断是否是对子
            if (pokerNumerArray[0] === pokerNumerArray[1] && pokerNumerArray[1] === pokerNumerArray[2]) {
                return [
                    pokerEnumFromNumber(pokerNumerArray[0])
                ]
            } else if (pokerNumerArray[1] - pokerNumerArray[0] === 1 && pokerNumerArray[2] - pokerNumerArray[1] === 1) {
                if (pokerMin === Poker_Number_2) {
                    // 2 3 -> 4 = 2,3,4
                    return [pokerEnumFromNumber(pokerMax + 1)]
                } else if (pokerMax === Poker_Number_A) {
                    // K A -> Q = Q,K,A
                    return [pokerEnumFromNumber(pokerMin - 1)]
                } else {
                    // 3 4 -> 2,5 = 2,3,4 | 3,4,5
                    return [pokerEnumFromNumber(pokerMin - 1), pokerEnumFromNumber(pokerMax + 1),]
                }
            } else {
                // 判断是不是中间空缺
                for (let idx = pokerMin + 1; idx < pokerMax; idx++) {
                    const pokerResult = pokerEnumFromNumber(idx);
                    if (pokerIsChain([...pokerArray, pokerResult[0]])) {
                        return [pokerResult];
                    }
                }
            }
        }
        return [];
    }


    /**
     * 检查是否是 对子/炸弹 2,2,2 , 3,3,3,3
     * @param pokers pokers
     * @param enableJoker 是否支持鬼牌
     * @returns 
     */
    export const pokerIsPairs = (pokers: EnumPoker[]) => {
        if (pokerJokerCount(pokers)) {
            console.log('检查::pokerIsPairs::存在鬼牌 ', pokers);
            return false;
        }
        const pokerNumberArray = pokers.map((poker) => pokerNumber(poker));
        for (let i = 1; i < pokerNumberArray.length; i++) {
            if (pokerNumberArray[i] !== pokerNumberArray[i - 1]) {
                return false
            }
        }
        return true;
    }

    /**
     * 检查是否是 链子 2,3,4  2 3
     * @param pokers pokers
     * @param enableJoker 是否支持鬼牌 2 J 3
     * @returns 
     */
    export const pokerIsChain = (pokers: EnumPoker[]): boolean => {
        if (pokerJokerCount(pokers)) {
            console.log('检查::pokerIsPairs::存在鬼牌 ', pokers);
            return false;
        }
        // 唯一排序逻辑（A始终作为14）
        const pokerNumberArray = pokers.map((poker) => pokerNumber(poker)).sort((a, b) => a - b);
        for (let i = 1; i < pokerNumberArray.length; i++) {
            if (pokerNumberArray[i] !== pokerNumberArray[i - 1] + 1) {
                return false
            }
        }
        return true;
    }

    /**
     * 检查颜色是否相同 , 红桃/方块 == 红色 , 梅花/黑桃 == 黑色
     * @param pokers 
     */
    export const pokerIsColor = (pokers: EnumPoker[]) => {
        return pokerColor(pokers).size === 1;
    }

    /**
     * 检查颜色是否相同 , 红桃/方块/大王 == 红色 , 梅花/黑桃/小王 == 黑色
     * @param pokers 
     */
    export const pokerColor = (pokers: EnumPoker[]) => {
        return new Set(pokers.map((poker) => {
            const pokerNumber = pokerValue(poker);
            const pokerType = pokerNumber - (pokerNumber % 100);
            return (pokerType === PokerConfig.Poker_Type_H || pokerType === PokerConfig.Poker_Type_F || poker === PokerConfig.J_B) ? true : false;
        }));
    }

    /**
     * 检查类型是否相同 , 红桃,红桃,红桃
     * @param pokers 
     */
    export const pokerIsType = (pokers: EnumPoker[]) => {
        return pokerType(pokers).size === 1;
    }

    /**
     * 检查类型是否相同 , 红桃,红桃,红桃
     * @param pokers 
     */
    export const pokerType = (pokers: EnumPoker[]): Set<EnumPokerType> => {
        return new Set(pokers.map((poker) => {
            const pokerNumber = pokerValue(poker);
            return (pokerNumber - (pokerNumber % 100)) as EnumPokerType;
        }));
    }

    export const pokerCheck = (type: string | null | undefined): EnumPoker | undefined => {
        if (typeof type !== 'string') return undefined
        for (let index = 0; index < pokerArray.length; index++) {
            const element = pokerArray[index];
            if (element === type) {
                return type as EnumPoker
            }
        }
        return undefined;
    }
}